package org.witsoft.dtx.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.witsoft.dtx.core.TransactionBranchHandler;
import org.witsoft.dtx.domain.BranchStatus;
import org.witsoft.dtx.domain.BranchTransaction;
import org.witsoft.dtx.domain.GlobalStatus;
import org.witsoft.dtx.domain.GlobalTransaction;
import org.witsoft.dtx.service.BranchTransactionService;
import org.witsoft.dtx.service.GlobalTransactionService;
import org.witsoft.dtx.service.TransactionManageService;

import java.util.List;

@Slf4j
public class TransactionManageServiceImpl implements TransactionManageService {

    @Autowired
    private GlobalTransactionService globalTxService;

    @Autowired
    private BranchTransactionService branchTxService;

    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void releaseLockTimeout(){
        int effects = branchTxService.releaseLockTimeout();
        if(effects > 0){
            log.info("成功释放"+ effects +"个超时事务分支锁");
        }
    }

    public void updateConfirmedToPrepare(){
        int effects = branchTxService.updateConfirmedToPrepare();
        if(effects > 0){
            log.info("成功释放"+ effects +"个已确认的分支事务");
        }
    }

    public void handle(int batchSize){
        List<BranchTransaction> branchTxList = branchTxService.findUnlockByStatus(BranchStatus.PREPARE, batchSize);
        if(!branchTxList.isEmpty()){
            log.info("准备执行{}个分支事务", branchTxList.size());
        }
        for (BranchTransaction btx : branchTxList) {
            try{
                TransactionBranchHandler handler = getHandler(btx.getHandlerClass());
                if(handler == null){
                    continue;
                }
                String gid = btx.getGid();
                String bid = btx.getBid();
                GlobalTransaction gtx = globalTxService.findById(gid);
                // 是否是准备状态，判断是否未超时
                if(gtx.getStatus() == GlobalStatus.PREPARE){
                    long timestamp = System.currentTimeMillis();
                    if(timestamp <= gtx.getCreateTime().getTime() + gtx.getTimeout()) {
                        continue;
                    }
                    log.warn("事务已超时:{}-{}", gid, bid);
                    gtx.setStatus(GlobalStatus.CANCELING);
                    globalTxService.updateStatus(gtx);
                }
                // 已取消或者已完成
                if(gtx.getStatus() == GlobalStatus.CANCELED || gtx.getStatus() == GlobalStatus.CONFIRMED){
                    continue;
                }
                //  尝试锁定记录
                if(!branchTxService.tryLock(gid, bid)){
                    continue;
                }
                try {
                    if(gtx.getStatus() == GlobalStatus.CANCELING) {
                        handler.cancel(gid, bid, btx.getHandlerArgs());
                        btx.setStatus(BranchStatus.CANCELED);
                        branchTxService.updateStatus(btx);
                        // 判断是否已完成全局任务
                        globalTxService.tryUpdateToCompleted(gid, GlobalStatus.CANCELED);
                    }else{
                        handler.confirm(gid, bid, btx.getHandlerArgs());
                        btx.setStatus(BranchStatus.CONFIRMED);
                        branchTxService.updateStatus(btx);
                        // 判断是否已完成全局任务
                        globalTxService.tryUpdateToCompleted(gid, GlobalStatus.CONFIRMED);
                    }
                }finally {
//                           // 2020/12/09 移除,改为超时释放，不马上释放
//                           branchTxService.unlock(gid, bid);
                }
            } catch(Throwable e){
                log.error("事务放弃任务失败", e);
            }
        }
    }

    public void purge(int minutesAgo) {
        String sql1 = "delete from dtx_branch where `create_time`  < date_sub(NOW(), interval "+ minutesAgo +" minute ) and status = 2 and lock_num > 0";
        String sql2 = "delete from dtx_global where status = 3 and id not in (select gid from dtx_branch)";
        this.jdbcTemplate.execute(sql1);
        this.jdbcTemplate.execute(sql2);
    }


    private TransactionBranchHandler getHandler(String className){
        try {
            Class<TransactionBranchHandler> clz = (Class<TransactionBranchHandler>) Class.forName(className);
            return beanFactory.getBean(clz);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}
